#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
数据匹配器实现
负责包装方案匹配算法和分类逻辑实现
"""

import pandas as pd
import re

from src.config.constants import (
    CLASSIFICATION_CATEGORIES,
    MATCH_STATUS,
    MATCHING_THRESHOLDS,
    REUSE_THRESHOLD,
    REFERENCE_THRESHOLD,
    FIELD_NAMES
)

from src.utils.text_utils import (
    calculate_jaccard_similarity,
    calculate_levenshtein_similarity
)

# 导入所需的辅助函数
from .classification import (
    _copy_package_info,
    _determine_product_category,
    _estimate_design_parameters,
    _find_closest_quantity_record,
    classify_products
)
from .combination_recommendation import (
    _process_combination_references,
    _process_mixed_references,
    recommend_combination_packages
)
from .package_type import identify_package_type
from .iterative_splitting import iterative_splitting


class DataMatcher:
    """负责包装方案匹配算法和分类逻辑实现"""

    def __init__(self):
        """初始化数据匹配器"""
        # 初始化匹配阈值
        self.jaccard_threshold = MATCHING_THRESHOLDS.get("JACCARD_SIMILARITY", 0.7)
        self.levenshtein_threshold = MATCHING_THRESHOLDS.get("LEVENSHTEIN_SIMILARITY", 0.8)
        self.reuse_threshold = REUSE_THRESHOLD
        self.reference_threshold = REFERENCE_THRESHOLD
        
        # 添加匹配权重配置
        self.similarity_weights = {
            "jaccard": 0.6,
            "levenshtein": 0.4
        }
        
        # 初始化日志记录
        import logging
        self.logger = logging.getLogger("data_matcher")
        self.logger.info("数据匹配器初始化完成")

    def preprocess_model_text(self, text):
        """预处理规格型号文本，去除特殊字符、空格等

        Args:
            text: 原始文本

        Returns:
            预处理后的文本
        """
        if not text or pd.isna(text):
            return ""
        
        # 转换为字符串并去除前后空格
        processed_text = str(text).strip()
        
        # 去除特殊字符和空格
        processed_text = re.sub(r'[\s\-\_\(\)\[\]\{\}\,\.\\/\+\*\#\@\!\?]', '', processed_text)
        
        # 转换为小写
        processed_text = processed_text.lower()
        
        return processed_text

    def calculate_weighted_similarity(self, task_model, hist_model):
        """计算加权综合相似度

        Args:
            task_model: 任务规格型号
            hist_model: 历史规格型号

        Returns:
            加权综合相似度得分
        """
        # 预处理文本
        processed_task = self.preprocess_model_text(task_model)
        processed_hist = self.preprocess_model_text(hist_model)
        
        if not processed_task or not processed_hist:
            return 0
        
        # 计算各种相似度
        jaccard_sim = calculate_jaccard_similarity(processed_task, processed_hist)
        levenshtein_sim = calculate_levenshtein_similarity(processed_task, processed_hist)
        
        # 特殊情况处理：完全相同的规格型号直接返回最高分
        if processed_task == processed_hist:
            return 1.0
        
        # 计算加权综合相似度得分
        combined_score = (
            self.similarity_weights["jaccard"] * jaccard_sim +
            self.similarity_weights["levenshtein"] * levenshtein_sim
        )
        
        return combined_score

    def update_matching_thresholds(self, **kwargs):
        """动态更新匹配阈值

        Args:
            **kwargs: 要更新的阈值参数，如reuse_threshold, reference_threshold等
        """
        for key, value in kwargs.items():
            if hasattr(self, key) and isinstance(value, (int, float)):
                setattr(self, key, value)
                self.logger.info(f"更新{key}为{value}")
        
        # 也可以更新相似度权重
        if "similarity_weights" in kwargs:
            new_weights = kwargs["similarity_weights"]
            for weight_key, weight_value in new_weights.items():
                if weight_key in self.similarity_weights and isinstance(weight_value, (int, float)):
                    self.similarity_weights[weight_key] = weight_value
                    self.logger.info(f"更新{weight_key}权重为{weight_value}")
            
            # 确保权重总和为1
            total_weight = sum(self.similarity_weights.values())
            if total_weight > 0:
                for weight_key in self.similarity_weights:
                    self.similarity_weights[weight_key] /= total_weight
                self.logger.info(f"更新后权重: {self.similarity_weights}")

    def analyze_matching_results(self, match_results):
        """分析匹配结果，生成统计报告

        Args:
            match_results: 匹配结果列表或DataFrame

        Returns:
            匹配结果分析报告字典
        """
        if isinstance(match_results, pd.DataFrame):
            # 从DataFrame中提取匹配类型信息
            match_types = match_results["备注"].value_counts()
            total_matches = len(match_results)
        else:
            # 从列表中提取匹配类型信息
            match_type_list = [result.get("备注", "") for result in match_results]
            match_types = pd.Series(match_type_list).value_counts()
            total_matches = len(match_results)
        
        # 构建分析报告
        analysis_report = {
            "总匹配数": total_matches,
            "复用项数量": match_types.get(CLASSIFICATION_CATEGORIES["REUSE"], 0),
            "参考项数量": (
                match_types.get(CLASSIFICATION_CATEGORIES["REFERENCE_COMBINATION"], 0) +
                match_types.get(CLASSIFICATION_CATEGORIES["REFERENCE_INDEPENDENT"], 0) +
                match_types.get(CLASSIFICATION_CATEGORIES["REFERENCE_MIXED"], 0)
            ),
            "设计项数量": match_types.get(MATCH_STATUS["DESIGN"], 0),
            "无匹配项数量": match_types.get("", 0)
        }
        
        # 计算比例
        if total_matches > 0:
            analysis_report["复用项比例"] = f"{analysis_report['复用项数量'] / total_matches * 100:.2f}%"
            analysis_report["参考项比例"] = f"{analysis_report['参考项数量'] / total_matches * 100:.2f}%"
            analysis_report["设计项比例"] = f"{analysis_report['设计项数量'] / total_matches * 100:.2f}%"
        else:
            analysis_report["复用项比例"] = "0%"
            analysis_report["参考项比例"] = "0%"
            analysis_report["设计项比例"] = "0%"
        
        self.logger.info(f"匹配结果分析报告: {analysis_report}")
        return analysis_report

    def match_specification(self, target_data, historical_data):
        """规格型号匹配

        Args:
            target_data: 目标数据（任务数据）
            historical_data: 历史包装数据

        Returns:
            包含匹配信息的DataFrame
        """
        if target_data is None or target_data.empty:
            self.logger.warning("目标数据为空")
            return pd.DataFrame()

        if historical_data is None or historical_data.empty:
            # 没有历史数据，所有记录都标记为设计项
            result = target_data.copy()
            result["备注"] = MATCH_STATUS["DESIGN"]
            self.logger.warning("历史数据为空，所有记录标记为设计项")
            return result

        # 创建结果DataFrame
        result = target_data.copy()
        result["备注"] = ""
        result["包装类型"] = ""
        result["匹配历史记录"] = None
        result["匹配得分"] = 0.0

        # 对每条目标记录进行规格型号匹配
        for idx, row in target_data.iterrows():
            specification = row.get(FIELD_NAMES["SPECIFICATION_MODEL"], "")
            quantity = row.get(FIELD_NAMES["QUANTITY"], 0)

            # 查找完全相同规格型号的历史记录
            exact_matches = historical_data[
                historical_data[FIELD_NAMES["SPECIFICATION_MODEL"]] == specification
            ]

            if not exact_matches.empty:
                # 完全匹配，标记为复用项
                exact_matches_with_score = exact_matches.copy()
                exact_matches_with_score["匹配得分"] = 1.0  # 规格型号完全匹配，设置匹配得分为1.0
                
                result.at[idx, "匹配历史记录"] = exact_matches_with_score
                result.at[idx, "匹配得分"] = 1.0
                result.at[idx, "备注"] = CLASSIFICATION_CATEGORIES["REUSE"]
                
                # 确定产品归类
                product_category = _determine_product_category(exact_matches)
                result.at[idx, "包装类型"] = product_category
            else:
                # 进行模糊匹配
                best_match_score = 0
                best_match_record = None
                
                # 为每条历史记录计算相似度
                for _, hist_row in historical_data.iterrows():
                    hist_spec = hist_row.get(FIELD_NAMES["SPECIFICATION_MODEL"], "")
                    
                    # 计算加权综合相似度
                    similarity_score = self.calculate_weighted_similarity(specification, hist_spec)
                    
                    # 如果相似度得分更高，则更新最佳匹配
                    if similarity_score > best_match_score:
                        best_match_score = similarity_score
                        # 创建包含相似度得分的历史记录副本
                        best_match_record = hist_row.copy()
                        best_match_record["匹配得分"] = similarity_score
                
                # 根据相似度得分确定匹配类型
                if best_match_score >= self.reuse_threshold:
                    # 高度相似，标记为复用项
                    result.at[idx, "匹配得分"] = best_match_score
                    result.at[idx, "匹配历史记录"] = pd.DataFrame([best_match_record])
                    result.at[idx, "备注"] = CLASSIFICATION_CATEGORIES["REUSE"]
                    
                    # 确定产品归类
                    product_category = _determine_product_category(pd.DataFrame([best_match_record]))
                    result.at[idx, "包装类型"] = product_category
                elif best_match_score >= self.reference_threshold:
                    # 中等相似，标记为参考项
                    result.at[idx, "匹配得分"] = best_match_score
                    result.at[idx, "匹配历史记录"] = pd.DataFrame([best_match_record])
                    result.at[idx, "备注"] = CLASSIFICATION_CATEGORIES["REFERENCE_INDEPENDENT"]
                    
                    # 确定产品归类
                    product_category = _determine_product_category(pd.DataFrame([best_match_record]))
                    result.at[idx, "包装类型"] = product_category
                else:
                    # 低相似或无相似，标记为设计项
                    result.at[idx, "匹配得分"] = best_match_score
                    result.at[idx, "备注"] = MATCH_STATUS["DESIGN"]
        
        # 生成匹配结果分析报告
        self.analyze_matching_results(result)

        return result

    def identify_package_type(self, data, merged_cells_info=None):
        """识别包装类型

        Args:
            data: 待识别的数据
            merged_cells_info: 合并单元格信息

        Returns:
            包含包装类型信息的数据
        """
        self.logger.info("执行包装类型识别")
        result = identify_package_type(data, merged_cells_info)
        self.logger.info("包装类型识别完成")
        return result
        
    def classify_data(self, data):
        """对数据进行产品归类

        Args:
            data: 待归类的数据

        Returns:
            归类后的数据
        """
        self.logger.info("执行产品归类")
        result = classify_products(data)
        self.logger.info("产品归类完成")
        return result
        
    def execute_matching_strategy(self, classified_data):
        """执行匹配策略，遵循项目大纲中的分类处理阶段要求

        Args:
            classified_data: 已分类的数据

        Returns:
            匹配结果
        """
        # 创建数据副本
        result_data = classified_data.copy()

        # 1. 处理复用项
        reuse_items = result_data[
            result_data["备注"] == CLASSIFICATION_CATEGORIES["REUSE"]
        ]
        if not reuse_items.empty:
            for idx, row in reuse_items.iterrows():
                historical_records = row["匹配历史记录"]
                if (
                    historical_records is not None
                    and not historical_records.empty
                ):
                    # 复用完全匹配的历史记录
                    best_match = historical_records.sort_values(
                        by="匹配得分", ascending=False
                    ).iloc[0]
                    _copy_package_info(result_data, best_match, idx)
                    # 标记为已复用
                    result_data.loc[idx, "复用标记"] = "是"

        # 2. 处理参考项（仅组合包装）
        combination_references = result_data[
            result_data["备注"]
            == CLASSIFICATION_CATEGORIES["REFERENCE_COMBINATION"]
        ]
        if not combination_references.empty:
            # 应用组合包装推荐逻辑
            result_data = _process_combination_references(result_data)

        # 3. 处理参考项（仅独立包装）
        independent_references = result_data[
            result_data["备注"]
            == CLASSIFICATION_CATEGORIES["REFERENCE_INDEPENDENT"]
        ]
        if not independent_references.empty:
            # 为独立包装参考项推荐最接近的方案
            for idx, row in independent_references.iterrows():
                historical_records = row["匹配历史记录"]
                if (
                    historical_records is not None
                    and not historical_records.empty
                ):
                    # 查找数量最接近的方案
                    closest_record = _find_closest_quantity_record(
                        historical_records, row["数量"]
                    )
                    if closest_record is not None:
                        # 复制包装方案信息
                        _copy_package_info(result_data, closest_record, idx)
                        # 标记为参考方案
                        result_data.loc[idx, "参考标记"] = "是"

        # 4. 处理参考项（混合包装）
        mixed_references = result_data[
            result_data["备注"] == CLASSIFICATION_CATEGORIES["REFERENCE_MIXED"]
        ]
        if not mixed_references.empty:
            # 应用混合包装推荐逻辑
            result_data = _process_mixed_references(result_data)

        # 5. 处理设计项
        design_items = result_data[
            result_data["备注"] == MATCH_STATUS["DESIGN"]
        ]
        if not design_items.empty:
            # 添加设计项标记
            result_data.loc[design_items.index, "设计标记"] = "是"
            # 估算包装参数（基于类似产品）
            result_data = _estimate_design_parameters(result_data)

        return result_data
